package ufmg.maze.aco;

import java.awt.Point;
import java.util.HashMap;
import java.util.Set;
import java.util.TreeSet;

import ufmg.maze.Maze;

public class Formiga {

	public static final byte NORTE = 0;
	public static final byte SUL = 1;
	public static final byte LESTE = 2;
	public static final byte OESTE = 3;
	public static int id = 1;
	
	private HashMap<Integer, Point> caminho;
	private byte[] genome;
	private int indiceFinal;
	private int identificador;
	private boolean chegou;

	public HashMap<Integer, Point> getCaminho() {
		return caminho;
	}

	public void setCaminho(HashMap<Integer, Point> caminho) {
		this.caminho = (HashMap<Integer, Point>)caminho.clone();
	}
	
	public void setGenome(byte[] genome) {
		this.genome = genome;
	}
	
	public void setIndiceFinal(int indiceFinal) {
		this.indiceFinal = indiceFinal;
	}

	public Formiga(int tamanho) {
		caminho = new HashMap<Integer, Point>();
		genome = new byte[tamanho];
		indiceFinal = -1;
		chegou = false;
		identificador = id++;
	}

	public int getIdentificador() {
		return identificador;
	}

	public void setIdentificador(int identificador) {
		this.identificador = identificador;
	}

	public Formiga(Formiga f) {
		this.genome = f.genome.clone();
		this.indiceFinal = f.indiceFinal;
		this.caminho = (HashMap<Integer, Point>)f.getCaminho().clone();
	}

	public byte[] getGenome() {
		return genome;
	}

	private byte deslocamentoX(byte movimento) {
		if (movimento == LESTE) {
			return 1;
		} else if (movimento == OESTE) {
			return -1;
		}
		return 0;
	}

	private byte deslocamentoY(byte movimento) {
		if (movimento == NORTE) {
			return -1;
		} else if (movimento == SUL) {
			return 1;
		}
		return 0;
	}

	public void popular(Maze maze) {
		caminho.clear();
		chegou = false;
		byte posX = 0;
		byte posY = 0;
		indiceFinal = 0;

		int tabX = maze.getInicialColuna();
		int tabY = maze.getInicialLinha();

		for (int k = 0; k < genome.length; k++) {
			byte direcao = -2;
			byte newPosX = 0;
			byte newPosY = 0;
			boolean b1 = false;
			boolean b2 = false;

			Set<Byte> direcoes = new TreeSet<Byte>();
			do {
				if(!maze.hasNexPosition(direcoes, tabY, tabX, direcao))
				{
					maze.transformaEmParede(tabY, tabX);
					return;
				}
				direcao = maze.nextPosition(tabY, tabX);
				direcoes.add(direcao);
				
				newPosX = deslocamentoX(direcao);
				newPosY = deslocamentoY(direcao);
				b1 = false;
				b2 = false;
				if (!maze.isValidMove(tabY, tabX, direcao) || (newPosX + posX == 0) && (newPosY + posY == 0)) {
					b1 = true;
				}
				else
				{
					tabX += 2 * newPosX;
					tabY += 2 * newPosY;
				}

				if ((tabX < 0 || tabX > maze.getLimiteX())
						|| (tabY < 0 || tabY > maze.getLimiteY())) {
					b2 = true;
					tabX -= 2 * newPosX;
					tabY -= 2 * newPosY;
				}
			} while (b1 || b2);

			if(maze.jaVisitou(this, tabY, tabX))
			{
				maze.transformaEmParede(tabY, tabX);
				return;
			}			
			maze.adicionaVisitante(this, tabY, tabX);
			
			posX = newPosX;
			posY = newPosY;
			
			caminho.put(k, new Point(tabX, tabY));

			genome[k] = direcao;
			indiceFinal = k;
			if(maze.getSaidaLinha()==tabY && maze.getSaidaColuna()==tabX)
			{
				chegou = true;
				break;
			}
		}
	}

	public boolean isChegou() {
		return chegou;
	}

	public void setChegou(boolean chegou) {
		this.chegou = chegou;
	}

	public static Formiga criar(int tamanho, Maze maze) {
		Formiga formiga = new Formiga(tamanho);
		formiga.popular(maze);
		return formiga;
	}

	public int getIndiceFinal() {
		return indiceFinal;
	}

	public void print(Maze maze) {
		System.out.println("-------------------------------------------------");
		System.out.println("Valor: "+caminho.size());
		System.out.println("Posição final: "+caminho.get(caminho.size()-1));
		System.out.println("-------------------------------------------------");
		System.out.println("Solução:");
		int x = maze.getInicialColuna();
		int y = maze.getInicialLinha();
		if (indiceFinal == -1) {
			System.out.println("Sem solução!");

		} else {
			int desXAnt = 0;
			int desYAnt = 0;
			for (int i = 0; i < genome.length; i++) {
				int desX = deslocamentoX(genome[i]);
				int desY = deslocamentoY(genome[i]);

				if (!(desX + desXAnt == 0 && desY + desYAnt == 0)
						&& maze.isValidMove(y, x, genome[i])) {

					desXAnt = desX;
					desYAnt = desY;
					printDirecao(genome[i]);
					switch (genome[i]) {
					case Formiga.NORTE:
						y = y - 2;
						break;
					case Formiga.SUL:
						y = y + 2;
						break;
					case Formiga.LESTE:
						x = x + 2;
						break;
					case Formiga.OESTE:
						x = x - 2;
						break;
					}
				}
			}
			System.out.println();
		}
	}

	private void printDirecao(int direcao) {
		switch (direcao) {
		case NORTE:
			System.out.print("N ");
			break;
		case SUL:
			System.out.print("S ");
			break;
		case LESTE:
			System.out.print("L ");
			break;
		case OESTE:
			System.out.print("O ");
			break;
		}
	}

	public Point getCelulaFeromonio(int j) {
		return caminho.get(j);
	}

	public int getTamanhoCaminho() {
		return caminho.size();
	}
}
